import type { tilingSchemeIdentify, ModelLoadType, CurrencyType, wmsImageType, wmtsImageType, xyzImageType, terrainType, GeoJsonLoadType, TileModelType } from '@/type/layerDecoratorType.d';
import * as Cesium from 'cesium'
window.Cesium = Cesium;
export class useLayerDecorator {
    /**
     * 地图对象 
     */
    public viewer: Cesium.Viewer;

    /**
     * @description 保存已添加的图层 
     * @description key:layer_id
     * @description value:Object
     */
    public LAYER_MAPS = new Map();

    /**
     * 默认的wmts服务的tile请求id
     */
    static TILEMATRIXSET_ID = "EPSG:4326"
    /**
     * 3dTiles的对象后缀名
     */
    static THREETILE_SUFFIX = "3DTILES"

    /**
     * @description ---
     * @param view 地图对象
     * @returns 
     */
    constructor(view: Cesium.Viewer) {
        if (!view) {
            throw new Error("请将地图对象传入--LayerDecorator")
        }

        this.viewer = view;

        return this;
    }

    // 根据不同的类型添加图层
    /**
     * 
     * @param params 
     * @param type 如果不传则使用参数中的类型
     */
    public useMain(params: any, type?: string) {

        let _type = type ? type : params.type;

        switch (_type) {
            case "wms":
                this.EditImageWMS(params);
                break;
            case "wmts":
                this.EditImageWMTS(params)
                break;
            case "xyz":
                this.EditImageXYZ(params)
                break;
            case "terrain":
                this.EditTerrainModel(params)
                break;
            case "3DTile":
                this.ThreeDTileModel(params)
                break;
            case "geo":
                this.LoadDataSourceGeoJson(params)
                break;
            case "model":
                this.LoadModelGLB(params)
                break;
            default:
                throw new Error("请输入正确的类型")
        }

    }
    /**
     * 添加wms图层
     * @param options 
     */
    public EditImageWMS(options: wmsImageType) {
        // 当参数中的 visable 为true时，添加图层

        if (options.visable) {
            let wmstProvider = new Cesium.WebMapServiceImageryProvider(options);

            let provider = this.viewer.imageryLayers.addImageryProvider(wmstProvider)

            this.LAYER_MAPS.set(options.id, provider)
        } else {
            let layer = this.LAYER_MAPS.get(options.id);

            layer && this.viewer.imageryLayers.remove(layer, false)

            layer && this.LAYER_MAPS.delete(options.id)
        }

    }
    /**
     * 添加wmts图层
     * @param options 
     */
    public EditImageWMTS(options: wmtsImageType) {
        // 当参数中的 visable 为true时，添加图层
        if (options.visable) {
            let _params = {
                tileMatrixSetID: useLayerDecorator.TILEMATRIXSET_ID,
                ...options,
                tilingScheme: this.getTilingSchemeByIdentify(options.tilingIdentify)
            }

            let wmstProvider = new Cesium.WebMapTileServiceImageryProvider(_params);

            let provider = this.viewer.imageryLayers.addImageryProvider(wmstProvider)

            this.LAYER_MAPS.set(options.id, provider)
        } else {
            let layer = this.LAYER_MAPS.get(options.id);

            layer && this.viewer.imageryLayers.remove(layer, false)
            layer && this.LAYER_MAPS.delete(options.id)
        }

    }

    /**
     * 根据参数添加{z,x,y}天地图
     * @param options 
     */
    public EditImageXYZ(options: xyzImageType) {

        // 当参数中的 visable 为true时，添加图层
        if (options.visable) {
            let _params = {
                ...options,
                tilingScheme: this.getTilingSchemeByIdentify(options.tilingIdentify)
            }
            let xyzProvider = new Cesium.UrlTemplateImageryProvider(_params);

            this.viewer.imageryLayers.addImageryProvider(xyzProvider)

            this.LAYER_MAPS.set(options.id, xyzProvider)
        } else {
            let layer = this.LAYER_MAPS.get(options.id);

            layer && this.viewer.imageryLayers.remove(layer, false)
            layer && this.LAYER_MAPS.delete(options.id)
        }
    }
    /**
     * 根据参数添加地形服务
     * @param options 
     */
    private async EditTerrainModel(options: terrainType) {
        // 当参数中的 visable 为true时，添加图层
        if (options.visable) {
            options.tilingScheme = this.getTilingSchemeByIdentify(options.tilingIdentify)

            let url = options.url;

            let provider = await Cesium.CesiumTerrainProvider.fromUrl(url, {
                ...options
            });

            this.viewer.terrainProvider = provider;
            
            // -------------------------  暂时性冗余区 开始 ------------------------
            /**
             *@description 这部分在最新版的API中是失效的，但是代码依然保留
             *@description 等找到新的解决方案，需要将这部分回调提示去掉
             */
            // 注册加载完成事件
            let CallBackAllLoad = options.allTilesLoaded instanceof Function ? options.allTilesLoaded : null;
            CallBackAllLoad && provider.readyPromise.then(function (event: any) {
                CallBackAllLoad(event);
            })
            // -------------------------  暂时性冗余区 结束 ------------------------

            this.LAYER_MAPS.set(options.id, provider)
        } else {
            let terrain = this.LAYER_MAPS.get(options.id);

            this.viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider(options || {})

            terrain && this.LAYER_MAPS.delete(options.id)
        }
    }

    /**
     * 3dTile
     * @param options 
     */
    private async ThreeDTileModel(options: TileModelType) {

        let scene = this.viewer.scene;
        // 当参数中的 visable 为true时，添加图层
        if (options.visable) {

            let tile = await Cesium.Cesium3DTileset.fromUrl(options.url);

            let tileset = scene.primitives.add(tile);

            // -------------------------  暂时性冗余区 开始 ------------------------
            // let tile = new Cesium.Cesium3DTileset(options);
            // 注册加载完事件
            let CallBackAllLoad = options.allTilesLoaded instanceof Function ? options.allTilesLoaded : null;

            CallBackAllLoad && tileset.allTilesLoaded.addEventListener(function () {
                CallBackAllLoad("success")
            });

            // 注册进度条事件
            let CallBackProgress = options.progressLoaded instanceof Function ? options.progressLoaded : null;
            CallBackProgress && tileset.loadProgress.addEventListener(function (numberOfPendingRequests: number, numberOfTilesProcessing: number) {
                CallBackProgress(numberOfPendingRequests, numberOfTilesProcessing, tileset)
            });
            // -------------------------  暂时性冗余区 结束 ------------------------

            options.isFlyTo && this.viewer.flyTo(tileset)

            this.LAYER_MAPS.set(`${options.id}_${useLayerDecorator.THREETILE_SUFFIX}`, tile)
            this.LAYER_MAPS.set(options.id, tileset)

        } else {

            let tileset = this.LAYER_MAPS.get(options.id);
            let tile = this.LAYER_MAPS.get(`${options.id}_${useLayerDecorator.THREETILE_SUFFIX}`);

            tileset && tileset.destroy();
            tile && scene.primitives.remove(tile);
            tileset && this.LAYER_MAPS.delete(options.id)
        }

    }

    /**
     * 添加geojson数据
     * @param options 
     */
    private async LoadDataSourceGeoJson(options: GeoJsonLoadType) {
        // 当参数中的 visable 为true时，添加图层

        if (options.visable) {

            let _fillColor = Cesium.Color.fromCssColorString(options.fillColor);
            let gjds = await Cesium.GeoJsonDataSource.load(options.url, {
                stroke: Cesium.Color.fromCssColorString(options.strokeColor) || Cesium.Color.HOTPINK,
                fill: Cesium.Color.fromAlpha(_fillColor, options.opacity),
                strokeWidth: options.strokeWidth || 3
            })

            this.viewer.dataSources.add(gjds)

            options.isFlyTo && this.viewer.flyTo(gjds)

            this.LAYER_MAPS.set(options.id, gjds)
        } else {
            let layer = this.LAYER_MAPS.get(options.id);

            layer && this.viewer.dataSources.remove(layer, false)

            this.LAYER_MAPS.delete(options.id)
        }
    }

    /**
     * 加载glb类型的模型
     * @param options 
     */
    private async LoadModelGLB(options: ModelLoadType) {
        let scene = this.viewer.scene;

        if (options.visable) {
            try {

                let _posi = options.position;

                let orgin = Cesium.Cartesian3.fromDegrees(_posi[0] || 0, _posi[1] || 0, _posi[2] || 0)
                // 设置模型的位置
                var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(orgin);

                const model = await Cesium.Model.fromGltfAsync({
                    ...options,
                    modelMatrix: modelMatrix,
                });

                scene.primitives.add(model);

                options.isFlyTo && this.viewer.camera.flyTo({
                    duration: options.duration || 1.0,
                    destination: orgin
                })

                // 注册加载完事件
                let CallBackAllLoad = options.allTilesLoaded instanceof Function ? options.allTilesLoaded : null;

                CallBackAllLoad && model.readyEvent.addEventListener((event: any) => {
                    CallBackAllLoad && CallBackAllLoad(event)
                });

                this.LAYER_MAPS.set(options.id, model)

            } catch (error) {
                throw new Error("加载-glb模型失败--" + error)
            }
        } else {
            let layer = this.LAYER_MAPS.get(options.id)
            layer && scene.primitives.remove(layer);
            layer && this.LAYER_MAPS.delete(options.id)
        }
    }

    /**
     * 获取默认地形服务
     * @param v 版本 默认使用高版本 1.107之后的，如果是1.107之前的，则传入--false
     */
    private async MakeDefaultTerrainService(v = true) {
        if (v) {
            const terrainProvider = await Cesium.createWorldTerrainAsync({
                requestWaterMask: true,
                requestVertexNormals: true,
            });
            this.viewer.terrainProvider = terrainProvider;
        } else {
            this.viewer.terrainProvider = Cesium.createWorldTerrain()
        }
    }

    /**
     * @description 根据标识返回平铺方案
     * @description WebMercatorTilingScheme这个平铺方案是 EPSG:3857的平铺方案
     * 我们平常常用的是经纬度来映射位置，因此这里使用EPSG:4326的方案，
     * 所以默认返回GeographicTilingScheme
     * @param identify  
     * @param options 
     * @returns 默认返回 GeographicTilingScheme 的平铺方案
     */
    private getTilingSchemeByIdentify(identify: tilingSchemeIdentify, options?: any) {
        if (identify == 'mer') {
            return new Cesium.WebMercatorTilingScheme(options || {})
        } else {
            return new Cesium.GeographicTilingScheme(options || {})
        }
    }

    /**
     * 检测当前标识关联的图层是否已被添加
     * @param identify 
     * @returns 
     */
    private hasLayerMap(identify: string | number) {
        return this.LAYER_MAPS.has(identify);
    }

    /**
     * 根据标识查找layer图层对象
     * @param identify 
     * @returns 
     */
    public getLayerFeature(identify: string | number) {
        return this.LAYER_MAPS.get(identify);
    }

}

/**
 * @description 根据不同的类型获取不同的加载服务参数
 * @description 注意：返回的数据为基本参数，具体的参数请查看类型文件或看官方文档
 */
export class useLayerParamsDecorator {

    constructor() {

    }

    /**
     * 根据类型返回相应的数据参数
     * @param type 
     * @returns 
     */
    public getParamsByType(type: string) {
        let result: object = {};
        switch (type) {
            case "wms":
                result = this.getImageWMSParams();
                break;
            case "wmts":
                result = this.getImagewmtsParams()
                break;
            case "xyz":
                result = this.getImagexyzParams()
                break;
            case "terrain":
                result = this.getTerrainParams()
                break;
            case "3DTile":
                result = this.getThreeDTileParams()
                break;
            case "geo":
                result = this.getDataSourceGeoJsonParams()
                break;
            case "model":
                result = this.getModelParams()
                break;
            default:
                break;
        }
        return result;
    }

    /**
     * 返回WMS服务参数
     * @returns 
     */
    private getImageWMSParams() {
        return {
            url: "图层地址",
            layers: "图层名",
            parameters: { format: "image/jpeg" },
            minimumLevel: 0,
            maximumLevel: 14,
        }
    }
    /**
     * 返回WMTS服务参数
     * @returns 
     */
    private getImagewmtsParams() {
        return {
            url: "服务地址",
            layer: "请求图层名",
            format: "格式:image/png或image/jpeg等等",
            style: "default",
            minimumLevel: 0,
            maximumLevel: 14,
        }
    }
    /**
     * 返回XYZ服务参数
     * @returns 
     */
    private getImagexyzParams() {
        return {
            url: "瓦片格式的请求地址?{z}&{x}&{y}",
        }
    }
    /**
     * 返回Terrain服务参数
     * @returns 
     */
    private getTerrainParams() {
        return {
            url: "地形服务的服务地址",
            requestVertexNormals: true,
        }
    }
    /**
     * 返回3DTile服务参数
     * @returns 
     */
    private getThreeDTileParams() {
        return {
            url: "3dTlie服务地址",
            // 是否飞入
            isFlyTo: true,
        }
    }
    /**
     * 返回GeoJson服务参数
     * @returns 
     */
    private getDataSourceGeoJsonParams() {
        return {
            url: "地址",
            strokeColor: "线的颜色-支持16进制",
            fillColor: "填充色-支持16进制",
            opacity: 0.2,
            isFlyTo: true,
        }
    }
    /**
     * 返回Model服务参数
     * @returns 
     */
    private getModelParams() {
        return {
            url: "服务地址",
            scale: 2.0,
            position: "数组，经纬度地址，如：[-75.628985, 39.555726, 1000]",
            isFlyTo: false,
        }
    }
}